package com.sunday.common.core.exception;

import com.sunday.common.core.constants.LogConstants;
import com.sunday.common.core.constants.StringConstants;
import com.sunday.common.core.enums.ErrorCodeEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;

import java.util.Optional;

/**
 * 核心异常
 *
 * @author zsy
 * @since 2022/10/14
 */
@Slf4j
@Data
public class CoreException extends RuntimeException {

    private static final String filterPointRegex;

    static {
        //获取上级包路径，作为过滤点
        String packageName = CoreException.class.getPackage().getName();
        filterPointRegex = StringConstants.HAT + packageName.substring(0, packageName.lastIndexOf(StringConstants.DOT) + 1) + StringConstants.STAR;
    }

    private String code;

    public CoreException(ErrorCodeEnum errorCodeEnum) {
        this(errorCodeEnum.errorCode, errorCodeEnum.errorMessage);
    }

    public CoreException(String code, String message) {
        super(message);
        this.code = code;
        this.realCaller();
    }

    /**
     * 默认开启替换
     *
     * @param errorCodeEnum
     * @param message
     */
    public CoreException(ErrorCodeEnum errorCodeEnum, @Nullable String message) {
        this(errorCodeEnum, message, false);
    }

    /**
     * 查看传入的错误信息是否存在，并且判定是追加消息还是替换
     *
     * @param errorCodeEnum
     * @param message
     * @param isAppend
     */
    public CoreException(ErrorCodeEnum errorCodeEnum, @Nullable String message, boolean isAppend) {
        this(errorCodeEnum.errorCode, errorCodeEnum.errorMessage, message, isAppend);
    }

    public CoreException(String errorCode, String errorMessage, @Nullable String message, boolean isAppend) {
        super(Optional.ofNullable(message)
                .filter(StringUtils::isNotBlank)
                .map(m -> isAppend ? errorMessage + "(" + m + ")" : m)
                .orElse(errorMessage));
        this.code = errorCode;
        this.realCaller();
    }

    private void realCaller() {
        for (StackTraceElement stackTraceElement : this.getStackTrace()) {
            if (!stackTraceElement.getClassName().matches(filterPointRegex)) {
                log.info(LogConstants.error_enum_format, this.code, this.getMessage(), stackTraceElement.toString());
                break;
            }
        }
    }

    @Override
    public String toString() {
        return getClass().getName() + ":(" + getCode() + "=" + getLocalizedMessage() + ")";
    }

}
